package com.hbut.friend.manager;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.hbut.common.core.constants.CacheConstants;
import com.hbut.common.core.constants.Constants;
import com.hbut.common.core.domain.TableDataInfo;
import com.hbut.common.core.enums.ExamListType;
import com.hbut.common.redis.service.RedisService;
import com.hbut.friend.domain.exam.Exam;
import com.hbut.friend.domain.exam.dto.ExamQueryDTO;
import com.hbut.friend.domain.exam.vo.ExamVO;
import com.hbut.friend.domain.user.User;
import com.hbut.friend.domain.user.UserExam;
import com.hbut.friend.mapper.exam.ExamMapper;
import com.hbut.friend.mapper.user.UserExamMapper;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class ExamCacheManager {

    @Autowired
    private ExamMapper examMapper;


//    @Autowired
//    private UserCacheManager userCacheManager;

    @Autowired
    private RedisService redisService;

    @Autowired
    private UserExamMapper userExamMapper;

    public Long getListSize(Integer examListType,Long userId){
        String examListKey = getExamListKey(examListType,userId);
        return redisService.getListSize(examListKey);
    }

    public List<ExamVO> getExamVOList(ExamQueryDTO examQueryDTO,Long userId){
        // 计算分页的起始和结束位置
        int start = (examQueryDTO.getPageNum()-1) * examQueryDTO.getPageSize();
        int end = examQueryDTO.getPageNum() * examQueryDTO.getPageSize();

        // 根据考试类型获取考试列表的缓存键
        String examListKey = getExamListKey(examQueryDTO.getType(),userId);

        // 从Redis中获取指定范围内的考试ID列表
        List<Long> examIdList = redisService.getCacheListByRange(examListKey, start, end, Long.class);

        // 根据考试ID列表组装考试信息列表
        List<ExamVO> examVOList = assembleExamVOList(examIdList);

        // 如果从Redis中获取的数据为空，则从数据库中获取考试信息列表，并刷新缓存
        if(CollectionUtils.isEmpty(examVOList)){
            examVOList = getExamListByDB(examQueryDTO,userId);
            refreshCache(examQueryDTO.getType(),userId);
        }

        // 返回考试信息列表
        return examVOList;
    }

    public List<Long> getAllUserExamList(Long userId) {
        String key = getUserExamKey(userId);
        List<Long> userExamIdList = redisService.getCacheListByRange(key, 0, -1, Long.class);
        if(CollectionUtil.isNotEmpty(userExamIdList)){
            return userExamIdList;
        }
        List<UserExam> userExamList =userExamMapper.selectList(new LambdaQueryWrapper<UserExam>()
                .eq(UserExam::getUserId,userId));
        if(CollectionUtil.isEmpty(userExamList)){
            return null;
        }
        refreshCache(ExamListType.USER_EXAM_LIST.getValue(),userId);
        return userExamList.stream().map(UserExam::getExamId).collect(Collectors.toList());
    }

    public void addUserExamCache(Long userId,Long examId){
        //这里要插入key,value
        String key = getUserExamKey(userId);
        redisService.leftPushForList(key,examId);
    }

    private String getUserExamKey(Long userId) {
        return CacheConstants.USER_EXAM_LIST+userId;
    }


    /**
     * 刷新考试列表缓存。
     *
     * 根据传入的考试列表类型（未完成考试列表或历史考试列表），从数据库中查询相应的考试列表，
     * 然后将考试对象批量存储到Redis中，并更新对应的考试列表缓存键。
     *
     * @param examListType 考试列表类型
     */
    public void refreshCache(Integer examListType,Long userId) {
        List<Exam> examList = new ArrayList<>();
        // 如果是未完成的考试列表
        if (ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)) {
            // 查询未完成的考试列表
            examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                    .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                    .gt(Exam::getEndTime, LocalDateTime.now()) // 结束时间大于当前时间
                    .eq(Exam::getStatus, Constants.TRUE) // 状态为有效
                    .orderByDesc(Exam::getCreateTime)); // 按创建时间降序排序
        }
        // 如果是历史考试列表
        else if (ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)) {
            // 查询历史考试列表
            examList = examMapper.selectList(new LambdaQueryWrapper<Exam>()
                    .select(Exam::getExamId, Exam::getTitle, Exam::getStartTime, Exam::getEndTime)
                    .le(Exam::getEndTime, LocalDateTime.now()) // 结束时间小于等于当前时间
                    .eq(Exam::getStatus, Constants.TRUE) // 状态为有效
                    .orderByDesc(Exam::getCreateTime)); // 按创建时间降序排序
        } else if (ExamListType.USER_EXAM_LIST.getValue().equals(examListType)) {
            List<ExamVO> examVOList = userExamMapper.selectUserExamList(userId);
            examList = BeanUtil.copyToList(examVOList, Exam.class);
        }
        // 如果考试列表为空，则直接返回
        if (CollectionUtils.isEmpty(examList)) {
            return;
        }
        // 创建一个映射，键为考试ID的字符串形式，值为对应的考试对象
        Map<String, Exam> examMap = new HashMap<>();
        // 创建一个列表，用于存储考试ID
        List<Long> examIdList = new ArrayList<>();
        // 遍历考试列表，构建映射和考试ID列表
        for (Exam exam : examList) {
            examMap.put(getDetailKey(exam.getExamId()), exam);
            examIdList.add(exam.getExamId());
        }
        // 将考试对象映射批量存储到Redis中
        redisService.multiSet(examMap);
        // 删除对应的考试列表缓存键
        redisService.deleteObject(getExamListKey(examListType,userId));
        // 将考试ID列表批量推送到对应的考试列表缓存键中
        redisService.rightPushAll(getExamListKey(examListType,userId), examIdList);
    }

    /**
     * 从数据库中获取考试列表
     *
     * @param examQueryDTO 查询条件对象
     * @return 考试列表
     */
    private List<ExamVO> getExamListByDB(ExamQueryDTO examQueryDTO,Long userId){
        // 使用PageHelper分页插件开始分页
        PageHelper.startPage(examQueryDTO.getPageNum(), examQueryDTO.getPageSize());
        if(ExamListType.USER_EXAM_LIST.getValue().equals(examQueryDTO.getType())){
            return userExamMapper.selectUserExamList(userId);
        }else{
            return examMapper.selectExamList(examQueryDTO);
        }
    }


    /**
     * 根据考试ID列表组装考试信息列表
     *
     * @param examIdList 考试ID列表
     * @return 组装后的考试信息列表，如果考试ID列表为空、从Redis中获取的数据为空、
     *         或者获取的数据数量与考试ID列表数量不一致，则返回null
     */
    public List<ExamVO> assembleExamVOList(List<Long> examIdList){
        // 如果考试ID列表为空，则返回null
        if(CollectionUtils.isEmpty(examIdList)){
            return null;
        }
        // 创建一个字符串列表，用于存储Redis中的详细信息键
        List<String> detailKeyList = new ArrayList<>();
        // 遍历考试ID列表，为每个考试ID生成详细信息键，并添加到detailKeyList中
        for(Long examId : examIdList){
            detailKeyList.add(getDetailKey(examId));
        }
        // 从Redis中批量获取考试信息列表
        List<ExamVO> examVOList = redisService.multiGet(detailKeyList, ExamVO.class);
        // 移除考试信息列表中的null元素
        CollUtil.removeNull(examVOList);
        // 如果考试信息列表为空，或者获取的数据数量与考试ID列表数量不一致，则返回null
        if(CollectionUtils.isEmpty(examVOList)||examVOList.size()!= examIdList.size()){
            return null;
        }
        // 返回组装后的考试信息列表
        return examVOList;
    }


    /**
     * 根据考试列表类型获取对应的缓存键
     *
     * @param examListType 考试列表类型
     * @return 对应的缓存键
     */
    private String getExamListKey(Integer examListType,Long userId) {
        if(ExamListType.EXAM_UN_FINISH_LIST.getValue().equals(examListType)){
            return CacheConstants.EXAM_UNFINISHED_LIST;
        }else if(ExamListType.EXAM_HISTORY_LIST.getValue().equals(examListType)){
            return CacheConstants.EXAM_HISTORY_LIST;
        }else{
            return CacheConstants.USER_EXAM_LIST + userId;
        }
    }




    private String getDetailKey(Long examId) {
        return CacheConstants.EXAM_DETAIL + examId;
    }



}
